/*!
# [serde2file](https://crates.io/crates/serde2file)'s derive macro

## usage / 用法

```ignore
#[derive(Serialize, Deserialize, Serde2File)]
#[serde2file(
    encrypt = "TestEncryptTool::encrypt",
    decrypt = "TestEncryptTool::decrypt",
    crypt_arg_type = "&'static str",
    file_name_getter_arg_type = "(&str,&str)",
    file_name_getter = "(&str,&str)",
    dump_file_name = "test_data.json",   
    file_name_getter = "some_get_file_name_function",
    file_name_getter_arg_type = "String"
)]
```

# Attributes / 属性 :

* #[serde2file(arg1=value1,...)]
  * encrypt Data encryption method
  * decrypt Data decryption method
  * The above encryption and decryption methods must be set at the same time, otherwise encryption and decryption will not be performed.
  * example : #[serde2file(encrypt="TestEncryptTool::encrypt",decrypt="TestEncryptTool::decrypt")]
  * dump_file_name
    * Custom dump file name
    * If not set ,the default dump file name is the full name of the current Struct.
    * For example, the default dump file name corresponding to serde2file::test::TestData is serde2file-test-TestData.json
    * example : #[serde2file(dump_file_name = "test_data.json")]
  * crypt_arg_type
    * Define additional encryption and decryption parameter types
    * Used to pass custom parameters to encryption or decryption functions
    * example : #[serde2file(crypt_arg_type = "&'static str")]
  * Dynamically determine the file name and save path
    * file_name_getter : File name personalization acquisition function
    * file_name_getter_arg_type : File name acquisition function parameter type, used to dynamically obtain file name according to parameters

* #[serde2file(参数1=值1,...)]
  * encrypt 数据加密方法
  * decrypt 数据解密方法
  * 以上加密和解密方法必须同时设置，否则不进行加解密。
  * 例如：#[file_encrypt(encrypt="TestEncryptTool::encrypt",decrypt="TestEncryptTool::decrypt")]
  * dump_file_name
    * 设置自定义的转储文件名称
    * 自定义转储文件名称
    * 默认为当前Struct的完整名称，
    *  如serde2file::test::TestData对应的缺省转储文件名称为serde2file-test-TestData.json
    * 例如：#[serde2file(dump_file_name = "test_data.json")]
  * crypt_arg_type
    * 定义额外的加解密参数类型
    * 用于向加密或解密函数传递自定义参数使用
    * 例如：#[serde2file(crypt_arg_type = "&'static str")]
  * 动态确定文件名称和保存路径
    * file_name_getter 文件名称获取函数
    * file_name_getter_arg_type 文件名称获取函数传参类型，用于根据参数动态获取文件名称
    * 例如：#[serde2file(file_name_getter = "some_get_file_name_function",file_name_getter_arg_type = "String")]

# Examples/例子

```ignore
use serde2file_macro_derive::Serde2File;

#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq, Serde2File)]
#[serde2file(
    encrypt = "TestEncryptTool::encrypt",
    decrypt = "TestEncryptTool::decrypt",
    crypt_arg_type = "&'static str",
    file_name_getter_arg_type = "(&str,&str)",
    file_name_getter = "(&str,&str)",
    dump_file_name = "test_data.json",   
    file_name_getter = "some_get_file_name_function",
    file_name_getter_arg_type = "String"
)]
struct TestData {
    id: String,
    name: String,
}
```
*/

use proc_macro::TokenStream;
use quote::quote;
use syn::{
    parse_macro_input, DeriveInput, Lit, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type,
};

#[proc_macro_derive(Serde2File, attributes(serde2file))]
pub fn serde_to_file_derive(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    impl_file_serde(&ast)
}

fn parse_ser2file_attrs(ast: &syn::DeriveInput) -> Vec<(String, String)> {
    ast.attrs.iter().find_map(|attr| {
        if attr.path.is_ident("serde2file") {
            attr.parse_meta().ok().map(|meta| {
                if let Meta::List(MetaList { nested, .. }) = meta {
                    let args: Vec<_> = nested
                        .iter()
                        .filter_map(|nested_meta| {
                            if let NestedMeta::Meta(Meta::NameValue(MetaNameValue {
                                path,
                                lit,
                                ..
                            })) = nested_meta
                            {
                                let name = path.get_ident().unwrap().to_string();
                                if path.is_ident("encrypt") || 
                                   path.is_ident("decrypt") || 
                                   path.is_ident("crypt_arg_type") || 
                                   path.is_ident("dump_file_name") ||
                                   path.is_ident("file_name_getter_arg_type") ||
                                   path.is_ident("file_name_getter") {
                                    match lit {
                                        Lit::Str(lit_str) => Some((name, lit_str.value())),
                                        _ => panic!(
                                            "serde2file attribute err,must be:encrypt、decrypt、crypt_arg_type、file_name_getter_arg_type、file_name_getter 、dump_file_name"
                                        ),
                                    }
                                } else {
                                    None
                                }
                            } else {
                                None
                            }
                        })
                        .collect();
                    args
                } else {
                    panic!("serde2file attribute err")
                }
            })
        } else {
            None
        }
    }).unwrap_or_default()
}
/// 获取某属性值
fn get_attr_value(attrs: &[(String, String)], name: &str) -> Option<String> {
    attrs
        .iter()
        .find_map(|attr| (attr.0 == name).then(|| attr.1.clone()))
}
fn impl_file_serde(ast: &syn::DeriveInput) -> TokenStream {
    let struct_name = &ast.ident;

    let (impl_generics, ty_generics, where_clause) = &ast.generics.split_for_impl();

    let ser2file_attrs = parse_ser2file_attrs(ast);

    // 自定义转储文件名称
    let dump_file_name = get_attr_value(&ser2file_attrs, "dump_file_name");
    // 获取加解密额外参数类型
    // 如果未设置额外加密类型，则该类型为()
    let encrypt_extra_arg_type = syn::parse_str::<Type>(
        &get_attr_value(&ser2file_attrs, "crypt_arg_type").unwrap_or_else(|| String::from("()")),
    )
    .expect("parse crypt extra arg type fail!");

    // 获取自定义文件名称用的参数类型
    // 如果未设置额外文件名称类型，则该类型为()
    let file_name_getter_arg_type = syn::parse_str::<Type>(
        &get_attr_value(&ser2file_attrs, "file_name_getter_arg_type")
            .unwrap_or_else(|| String::from("()")),
    )
    .expect("parse file name getter arg type fail!");

    // 序列化trait 类型定义
    let type_define_code = quote! {
        type CryptExtraArgType = #encrypt_extra_arg_type;
        type FileNameGetterArgType = #file_name_getter_arg_type;
    };

    // 完全自定义获取文件名称函数定义部分代码
    let file_name_getter = get_attr_value(&ser2file_attrs, "file_name_getter");
    let file_name_getter_code = if let Some(file_name_getter) = file_name_getter {
        let file_name_getter = syn::parse_str::<Path>(&file_name_getter).expect("parse file name getter method fail!");        
        quote! {
            const FILE_NAME_GETTER: Option<FileNameGetter<Self::FileNameGetterArgType>> = Some(#file_name_getter);
        }
    } else {
        quote! {
            const FILE_NAME_GETTER: Option<FileNameGetter<Self::FileNameGetterArgType>> = None;
        }
    };
    let encrypt_fn = get_attr_value(&ser2file_attrs, "encrypt");
    let decrypt_fn = get_attr_value(&ser2file_attrs, "decrypt");
    // 加密方法定义部分代码
    let crypt_code = if let (Some(encrypt_fn), Some(decrypt_fn)) = (encrypt_fn, decrypt_fn) {
        let encrypt_fn = syn::parse_str::<Path>(&encrypt_fn).expect("parse encrypt method fail!");
        let decrypt_fn = syn::parse_str::<Path>(&decrypt_fn).expect("parse decrypt method fail!");
        
        quote! {
            const ENCRYPT: Option<EncryptMethod<Self::CryptExtraArgType>> = Some(#encrypt_fn);
            const DECRYPT: Option<DecryptMethod<Self::CryptExtraArgType>> = Some(#decrypt_fn);
        }
    } else {
        quote! {
            const ENCRYPT: Option<EncryptMethod<Self::CryptExtraArgType>> = None;
            const DECRYPT: Option<DecryptMethod<Self::CryptExtraArgType>> = None;
        }
    };
    // 存储文件自定义固定名称
    let dump_file_name_code = if let Some(dump_file_name) = dump_file_name {
        quote! {
            const DUMP_FILE_NAME: Option<&'static str> = Some(#dump_file_name);
        }
    } else {
        quote! {
            const DUMP_FILE_NAME: Option<&'static str> = None;
        }
    };
    quote! {
        impl  #impl_generics FileSerde for #struct_name #ty_generics #where_clause{
            #type_define_code

            #crypt_code
            #file_name_getter_code

            #dump_file_name_code
        }
    }
    .into()
}
